package sg.simple.snmp.provider.impl;

import isnmp.ui.dto.CPUInfo;
import isnmp.ui.dto.DeviceInfo;
import isnmp.ui.dto.StorageInfo;
import isnmp.ui.dto.MachineInfo;
import isnmp.ui.dto.ProgramInfo;
import isnmp.ui.dto.NetworkInfo;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.snmp4j.CommunityTarget;
import org.snmp4j.Snmp;
import org.snmp4j.TransportMapping;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OctetString;
import org.snmp4j.transport.DefaultUdpTransportMapping;

import sg.simple.snmp.provider.IpDiscoveryCallback;
import sg.simple.snmp.provider.NetMapException;
import sg.simple.snmp.provider.SimpleSNMPQueries;
import sg.simple.snmp.stats.OidCode;
import sg.simple.snmp.util.Constants;
import sg.simple.snmp.util.NetworkUtils;

public class SimpleSNMPQueriesImpl implements SimpleSNMPQueries {

	Logger logger = Logger.getLogger(SimpleSNMPQueriesImpl.class);

	@Override
	public void listIPsAsync(final String subnet, final int timeoutInMillies,
			final IpDiscoveryCallback callback) throws NetMapException {
		NetworkUtils.instance().discoverActiveIpsAsync(subnet,
				timeoutInMillies, callback);
	}

	@Override
	public MachineInfo getMachineInfo(String ipAddress,
			int timeoutInMillies, String community) throws NetMapException {

		Snmp snmp = null;
		MachineInfo machineInfo = new MachineInfo(ipAddress);
		try {
			Address targetAddress = GenericAddress.parse(String.format(
					Constants.SNMP_ADDRESS_FORMAT, ipAddress, 161));

			TransportMapping transport = new DefaultUdpTransportMapping();
			snmp = new Snmp(transport);
			transport.listen();

			CommunityTarget target = configureTarget(timeoutInMillies,
					community, targetAddress);

			MachineSummaryInfoProvider machineSummaryInfoProvider = new MachineSummaryInfoProvider();

			Map<OidCode, Object> summary = machineSummaryInfoProvider
					.readSummary(ipAddress, snmp, target);
			machineInfo.setValues(summary);

		} catch (Exception e) {
			throw new NetMapException("unable to retrieve machine information from "+ipAddress,
					e);
		} finally {
			if (snmp != null) {
				try {
					snmp.close();
				} catch (IOException e) {
					logger.error(
							"error occurred while closing snmp session - will be ignored",
							e);
				}
			}
		}

		return machineInfo;

	}

	private CommunityTarget configureTarget(int timeoutInMillies,
			String community, Address targetAddress) {
		// setting up target
		CommunityTarget target = new CommunityTarget();
		target.setCommunity(new OctetString(community));
		target.setAddress(targetAddress);
		target.setRetries(Constants.NUMBER_OF_SNMP_RETRIES);
		target.setTimeout(timeoutInMillies);
		target.setVersion(SnmpConstants.version2c);
		return target;
	}

	@Override
	public void shutdownMachine(String ipAddress) throws NetMapException {

	}

	@Override
	public void restartMachine(String ipAddress) throws NetMapException {

	}

	@Override
	public void blockMachine(String ipAddress) throws NetMapException {

	}

	@Override
	public boolean isMachineAlive(String ipAddress, int timeoutInMillies) {

		boolean isAlive = false;

		try {
			isAlive = InetAddress.getByName(ipAddress).isReachable(
					timeoutInMillies);
		} catch (UnknownHostException e) {
			logger.error("unable to find host", e);
			e.printStackTrace();
		} catch (IOException e) {
			logger.error(
					"Io exception occured while trying check connectivity", e);
		}

		return isAlive;
	}

	@Override
	public List<CPUInfo> getCPUInfo(String ipAddress, int timeoutInMillies,
			String community) throws NetMapException {

		Snmp snmp = null;
		List<CPUInfo> cpuInfoList = new ArrayList<CPUInfo>();
		try {
			Address targetAddress = GenericAddress.parse(String.format(
					Constants.SNMP_ADDRESS_FORMAT, ipAddress, 161));

			TransportMapping transport = new DefaultUdpTransportMapping();
			snmp = new Snmp(transport);
			transport.listen();

			CommunityTarget target = configureTarget(timeoutInMillies,
					community, targetAddress);

			cpuInfoList = new CPUInfoProvider().retrieve(snmp, target);

		} catch (Exception e) {
			throw new NetMapException("unable to retrieve cpu information from "+ipAddress,
					e);
		} finally {
			if (snmp != null) {
				try {
					snmp.close();
				} catch (IOException e) {
					logger.error(
							"error occurred while closing snmp session - will be ignored",
							e);
				}
			}
		}

		return cpuInfoList;
	}

	@Override
	public List<ProgramInfo> getProgramInfo(String ipAddress,
			int timeoutInMillies, String community) throws NetMapException {

		Snmp snmp = null;
		List<ProgramInfo> memInfoList = new ArrayList<ProgramInfo>();
		try {
			Address targetAddress = GenericAddress.parse(String.format(
					Constants.SNMP_ADDRESS_FORMAT, ipAddress, 161));

			TransportMapping transport = new DefaultUdpTransportMapping();
			snmp = new Snmp(transport);
			transport.listen();

			CommunityTarget target = configureTarget(timeoutInMillies,
					community, targetAddress);

			memInfoList = new ProgramInfoProvider().retrieve(snmp, target);

		} catch (Exception e) {
			throw new NetMapException("unable to retrieve program information from "+ipAddress,
					e);
		} finally {
			if (snmp != null) {
				try {
					snmp.close();
				} catch (IOException e) {
					logger.error(
							"error occurred while closing snmp session - will be ignored",
							e);
				}
			}
		}

		return memInfoList;

	}

	@Override
	public List<NetworkInfo> getNeworkInfo(String ipAddress,
			int timeoutInMillies, String community) throws NetMapException {
		Snmp snmp = null;
		List<NetworkInfo> networkInfoList = new ArrayList<NetworkInfo>();
		try {
			Address targetAddress = GenericAddress.parse(String.format(
					Constants.SNMP_ADDRESS_FORMAT, ipAddress, 161));

			TransportMapping transport = new DefaultUdpTransportMapping();
			snmp = new Snmp(transport);
			transport.listen();

			CommunityTarget target = configureTarget(timeoutInMillies,
					community, targetAddress);

			networkInfoList = new NetworkInfoProvider().retrieve(snmp, target);

		} catch (Exception e) {
			throw new NetMapException("unable to retrieve network information from "+ipAddress,
					e);
		} finally {
			if (snmp != null) {
				try {
					snmp.close();
				} catch (IOException e) {
					logger.error(
							"error occurred while closing snmp session - will be ignored",
							e);
				}
			}
		}

		return networkInfoList;
	}

	@Override
	public List<StorageInfo> getStorageInfo(String ipAddress,
			int timeoutInMillies, String community) throws NetMapException {
		Snmp snmp = null;
		List<StorageInfo> storageInfoList = new ArrayList<StorageInfo>();
		try {
			Address targetAddress = GenericAddress.parse(String.format(
					Constants.SNMP_ADDRESS_FORMAT, ipAddress, 161));

			TransportMapping transport = new DefaultUdpTransportMapping();
			snmp = new Snmp(transport);
			transport.listen();

			CommunityTarget target = configureTarget(timeoutInMillies,
					community, targetAddress);

			storageInfoList = new StorageInfoProvider().retrieve(snmp, target);

		} catch (Exception e) {
			throw new NetMapException("unable to retrieve storage information from "+ipAddress,
					e);
		} finally {
			if (snmp != null) {
				try {
					snmp.close();
				} catch (IOException e) {
					logger.error(
							"error occurred while closing snmp session - will be ignored",
							e);
				}
			}
		}

		return storageInfoList;
	}

	@Override
	public List<DeviceInfo> getDeviceInfo(String ipAddress,
			int timeoutInMillies, String community) throws NetMapException {
		Snmp snmp = null;
		List<DeviceInfo> deviceInfoList = new ArrayList<DeviceInfo>();
		try {
			Address targetAddress = GenericAddress.parse(String.format(
					Constants.SNMP_ADDRESS_FORMAT, ipAddress, 161));

			TransportMapping transport = new DefaultUdpTransportMapping();
			snmp = new Snmp(transport);
			transport.listen();

			CommunityTarget target = configureTarget(timeoutInMillies,
					community, targetAddress);

			deviceInfoList = new DeviceInfoProvider().retrieve(snmp, target);

		} catch (Exception e) {
			throw new NetMapException("unable to retrieve device information from "+ipAddress,
					e);
		} finally {
			if (snmp != null) {
				try {
					snmp.close();
				} catch (IOException e) {
					logger.error(
							"error occurred while closing snmp session - will be ignored",
							e);
				}
			}
		}

		return deviceInfoList;
	}

}
